package com.coolxiaoyao.web.http;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.ipfilter.IpFilterRule;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Kerry on 18/09/19
 */

public class HttpServer {


    private static Logger logger = LoggerFactory.getLogger(HttpServer.class);

    private final DispatcherServlet dispatcherServlet;
    private IpFilterRule ipFilterRule = null;
    private final int httpPort;
    private EventLoopGroup boss;
    private EventLoopGroup work;
    private final int workCount;

    public HttpServer(int httpPort, DispatcherServlet dispatcherServlet) {
        this(httpPort, dispatcherServlet, 0);
    }


    public HttpServer(int httpPort, DispatcherServlet dispatcherServlet, int workCount) {
        this.httpPort = httpPort;
        this.workCount = workCount;
        this.dispatcherServlet = dispatcherServlet;
    }


    public void startNetty() {

        logger.info("HttpServer Start On Port = {}", httpPort);

        boss = new NioEventLoopGroup(1);
        work = new NioEventLoopGroup(workCount);
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(boss, work).channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast("http-decoder", new HttpRequestDecoder());
                        pipeline.addLast("http-aggregator", new HttpObjectAggregator(65535));
                        pipeline.addLast("http-encoder", new HttpResponseEncoder());
                        pipeline.addLast("http-chunk", new ChunkedWriteHandler());
                        pipeline.addLast("http-handler", new HttpServerHandler(dispatcherServlet, ipFilterRule));
                    }
                });
        try {
            ChannelFuture sync = serverBootstrap.bind(httpPort).sync();
            // .addListener(future -> logger.info("HttpServer Start Status isSuccess = {}", future.isSuccess()));
            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            logger.error(e.getMessage(), e);
            e.printStackTrace();
        } finally {
            stopNetty();
        }
    }

    public void setIpFilterRule(IpFilterRule ipFilterRule) {
        this.ipFilterRule = ipFilterRule;
    }

    public IpFilterRule getIpFilterRule() {
        return ipFilterRule;
    }

    public void stopNetty() {
        if (boss != null) {
            boss.shutdownGracefully();
        }
        if (work != null) {
            work.shutdownGracefully();
        }
    }
}
